package reactor

import (
	"context"
	"gitee.com/lipore/go-nio/pkg/channel"
	"gitee.com/lipore/go-nio/pkg/events"
	"gitee.com/lipore/go-nio/pkg/transport"
	"gitee.com/lipore/plume/logger"
)

type (
	Reactor interface {
		Serve() error
		Close() error
		AddChannelGroup(cg channel.Group)
	}
)

type reactor struct {
	ctx    context.Context
	cancel context.CancelFunc

	acceptor transport.Acceptor

	readyChannelIds chan int64
	chanGroups      channel.Groups
}

func NewReactor(ctx context.Context, acceptor transport.Acceptor) Reactor {
	rCtx, rCancel := context.WithCancel(ctx)

	return &reactor{
		ctx:    rCtx,
		cancel: rCancel,

		acceptor: acceptor,

		readyChannelIds: make(chan int64),
		chanGroups:      make([]channel.Group, 0),
	}
}

func (r *reactor) Serve() error {
	for {
		select {
		case <-r.ctx.Done():
			return nil
		default:
			t, err := r.acceptor.Accept()
			if err != nil {
				logger.Warnf("%v", err)
				continue
			}
			c := channel.NewChannel(nil, t)
			if len(r.chanGroups) > 0 {
				err = r.chanGroups[0].Add(c)
				if err != nil {
					_ = c.Close()
				}
			}
			if c.Pipeline() != nil {
				c.Pipeline().FireChannelEvent(c, events.ConnectEvent)
			}
		}
	}
}

func (r *reactor) Close() error {
	var e error
	err := r.acceptor.Close()
	if err != nil {
		e = err
	}
	for _, cg := range r.chanGroups {
		err = cg.Close()
		if err != nil {
			e = err
		}
	}
	r.cancel()
	return e
}

func (r *reactor) AddChannelGroup(cg channel.Group) {
	r.chanGroups = append(r.chanGroups, cg)
}
